package com.qingfeng.coupon.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.qingfeng.client.DO.GoodsInfoDo;
import com.qingfeng.client.DO.PreTradeDo;
import com.qingfeng.client.bean.Buyer;
import com.qingfeng.client.bean.OrderStore;
import com.qingfeng.client.bean.Trade;
import com.qingfeng.client.dao.OrderStoreMapper;
import com.qingfeng.client.util.MakeOrderNum;
import com.qingfeng.coupon.Do.CouponDetail;
import com.qingfeng.coupon.constant.CouponCategory;
import com.qingfeng.coupon.constant.CouponExpireType;
import com.qingfeng.coupon.constant.CouponUseType;
import com.qingfeng.coupon.dao.CouponMapper;
import com.qingfeng.coupon.dao.CouponTemplateMapper;
import com.qingfeng.coupon.entity.Coupon;
import com.qingfeng.coupon.entity.CouponTemplate;
import com.qingfeng.exception.LsException;
import com.qingfeng.util.CodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 优惠券分发，
 * 核算服务
 *
 * @author zhangyuefei
 * @version 1.0
 * @date 2020/5/28 10:43 上午
 */
@Slf4j
@Service
public class CouponService {

    @Autowired
    private CouponTemplateMapper templateMapper;

    @Autowired
    private CouponMapper couponMapper;

    @Autowired
    private OrderStoreMapper orderStoreMapper;

    /**
     * 使用优惠券，将优惠券状态修改为已使用
     *
     * @param couponId
     */
    public void useCoupon(Long couponId) {
        log.warn("delete coupon couponid:" + couponId);
        couponMapper.updateStatusById(2, couponId);
    }

    /**
     * 获取优惠券优惠金额，
     * 更新单个商品优惠金额和实际付款金额
     *
     * @param goodsInfoDos
     * @param couponId
     * @return
     */
    public CouponDetail getCouponMoney(List<GoodsInfoDo> goodsInfoDos, Long couponId, Long buyerId) {
        if (couponId == null) return null;
        CouponDetail couponDetail = couponMapper.selectDetailById(couponId, buyerId);
        if (couponDetail == null) {
            throw new LsException(CodeEnum.ERROR, "优惠券不存在或已被使用");
        }
        switch (couponDetail.getUseType()) {
            case 1:
                return amountUseSaveMoney(goodsInfoDos, couponDetail);
            case 2:
                return goodsUseSaveMoney(goodsInfoDos, couponDetail);
            case 3:
                return goodsTypeUseSaveMoney(goodsInfoDos, couponDetail);
        }
        return null;
    }


    /**
     * 筛选出当前订单所有可用优惠券，
     * 并且把优惠金额最大的排在最前面
     * 1. 筛选出当前可用的优惠券
     * 满减金额对计算
     * 指定商品进行计算
     * 指定商品分类计算
     * 2. 对优惠金额进行排序
     *
     * @param preTradeDo
     * @param buyerId
     * @return
     */
    public List<CouponDetail> orderUsableCoupon(PreTradeDo preTradeDo, Long buyerId) {
        List<CouponDetail> list = new ArrayList<>();
        List<CouponDetail> usableList = getUsabelTemplate(buyerId);
        List<GoodsInfoDo> goodsInfoDos = preTradeDo.getGoodsInfoDos();
        if (goodsInfoDos == null) {
            throw new LsException(CodeEnum.ERROR, "当前订单下无商品");
        }
        if (CollectionUtils.isEmpty(usableList)) {
            log.error("[couponService-orderUsableCoupon] usableList is null");
            return list;
        }

        log.info("=================未过期优惠券：{}",JSON.toJSONString(usableList));
        // 筛选出全场满减优惠券
        log.info("=================全场满减优惠券");
        list.addAll(getAmountCouponByTrade(goodsInfoDos, usableList));
        // 筛选出指定商品优惠券
        log.info("=================指定商品优惠券");
        list.addAll(getGoodsCouponByTrade(goodsInfoDos, usableList));
        // 筛选出指定商品类型优惠券
        log.info("=================指定商品优惠券");
        list.addAll(getGoodsTypeCouponByTrade(goodsInfoDos, usableList));

        log.info("==========满足订单的优惠券如下{}",JSON.toJSONString(list));
        //按照折扣金额降序排序，去重
        list = list.stream().sorted(
                Comparator.comparing(CouponDetail::getDiscountQuota).reversed()
        ).distinct().collect(Collectors.toList());

        return list;
    }


    /**
     * 根据交易选出满减优惠券
     *
     * @return
     */
    private List<CouponDetail> getAmountCouponByTrade(
            List<GoodsInfoDo> goodsInfoDos, List<CouponDetail> usableList) {
        // 计算全场满减优惠券
        int sum = goodsInfoDos.stream().mapToInt(item ->
                item.getNum() * item.getPrice()).sum();
        log.info("==============全场满减优惠，订单总价为"+sum);
        List<CouponDetail> sumCouponList = usableList.stream().filter(item ->
                item.getDiscountBase() <= sum
                        && item.getUseType().equals(CouponUseType.AMOUNT_USE.getCode())
        ).collect(Collectors.toList());
        log.info("==============符合满减的优惠券如下，{}", JSONArray.toJSONString(sumCouponList));
        return sumCouponList;
    }

    /**
     * 根据交易选出指定商品使用优惠券
     * 1. 遍历 可以使用的优惠券模版，
     * 2。获取指定商品模版的所有指定商品的goodsCommondId组成list
     * 3。计算商品id在2的list中的所有商品的总价
     * 4。判断总价是否符合基准
     *
     * @param goodsInfoDos
     * @param usableList
     * @return
     */
    private List<CouponDetail> getGoodsCouponByTrade(
            List<GoodsInfoDo> goodsInfoDos, List<CouponDetail> usableList) {
        List<CouponDetail> list = new ArrayList<>();
        for (CouponDetail couponDetail : usableList) {
            if (couponDetail.getUseType().equals(CouponUseType.GOODS_USE.getCode())) {
                List<Long> goodsCommonIds = couponDetail.getUseGoods()
                        .stream()
                        .map(item -> item.getGoodsCommonId())
                        .collect(Collectors.toList());
                int sum = goodsInfoDos.stream().mapToInt(item -> {
                    if (goodsCommonIds.contains(item.getGoodsCommonId())) {
                        return item.getPrice() * item.getNum();
                    }
                    return 0;
                }).sum();
                log.info("======商品总价为"+sum);
                if (couponDetail.getDiscountBase() <= sum) {
                    log.info("======符合条件的优惠券模版{}",JSON.toJSONString(couponDetail));
                    list.add(couponDetail);
                }else {
                    log.info("======不符合条件的优惠券模版{}",JSON.toJSONString(couponDetail));
                }
            }
        }
        return list;
    }

    /**
     * 根据交易选出满减优惠券
     *
     * @return
     */
    private List<CouponDetail> getGoodsTypeCouponByTrade(
            List<GoodsInfoDo> goodsInfoDos, List<CouponDetail> usableList) {
        List<CouponDetail> list = new ArrayList<>();
        for (CouponDetail couponDetail : usableList) {
            if (couponDetail.getUseType().equals(CouponUseType.GOODS_TYPE_USE.getCode())) {
                List<Integer> goodsType = couponDetail.getUseGoods().stream()
                        .map(item -> item.getGoodsType())
                        .collect(Collectors.toList());
                int sum = goodsInfoDos.stream().mapToInt(item -> {
                    if (goodsType.contains(item.getTypeId())) {
                        return item.getPrice() * item.getNum();
                    }
                    return 0;
                }).sum();
                log.info("======订单中商品总价为"+sum);
                if (couponDetail.getDiscountBase() <= sum) {
                    log.info("======符合条件的优惠券模版{}",JSON.toJSONString(couponDetail));
                    list.add(couponDetail);
                }else {
                    log.info("======不符合条件的优惠券模版{}",JSON.toJSONString(couponDetail));
                }
            }
        }
        return list;
    }


    /**
     * 获取所有可分发优惠券
     * 1.获取所有可分发优惠券
     * 2.判断每个优惠券，当前用户是否领取过，如果是则修改状态
     *
     * @param goodsCommonId
     * @param buyer
     * @return
     */
    public List<CouponTemplate> getDistributeTemplates(Long goodsCommonId, Buyer buyer) {

        List<CouponTemplate> list = templateMapper.getByDistributeGoodsId(goodsCommonId);
        if (CollectionUtils.isEmpty(list)) {
            return list;
        }
        List<CouponDetail> useableList = getUsabelTemplate(buyer.getId());
        Iterator<CouponTemplate> it = list.iterator();
        while (it.hasNext()) {
            CouponTemplate couponTemplate = it.next();
//             检查优惠券当前用户是否可以领取
            try {
                checkReceive(couponTemplate, buyer.getId());
            } catch (LsException e) {
                log.error("get distribute Templates error:" + e.getMessage());
                it.remove();
                continue;
            }

            couponTemplate.setReceived(false);
            for (CouponDetail couponDetail : useableList) {
                if (couponDetail.getId().equals(couponTemplate.getId())) {
                    couponTemplate.setReceived(true);
                }
            }
        }

        return list;
    }


    /**
     * 获取当前用户所有可用优惠券模版
     * 并更新过期优惠券
     *
     * @param buyerId
     * @return
     */
    public List<CouponDetail> getUsabelTemplate(Long buyerId) {
        //查询所有未使用的优惠券
        List<CouponDetail> mycouponList = couponMapper.selectByUserId(buyerId, 1);
        //更新过期优惠券
        updateExpiredCoupon(mycouponList);

        // 可使用优惠券list
        List<CouponDetail> usableList = new ArrayList<>();
                mycouponList.stream().forEach(item -> {
                    if (item.getExpirePeriod().equals(CouponExpireType.FIX_DATA.getCode())) {
                        item.setStart(item.getAssignTime());
                        long endtime = item.getAssignTime().getTime()
                                + (item.getExpireGap() * 24 * 3600000L);
                        item.setEnd(new Date(endtime));
                        //如果是固定间隔日期，则判断当前天数是否过期
                        if (endtime > new Date().getTime()) {
                            usableList.add(item);
                        }
                    } else {
                        item.setStart(item.getExpireStart());
                        item.setEnd(item.getExpireEnd());
                        if (item.getAssignTime().getTime()
                                < item.getExpireEnd().getTime() &&
                                item.getAssignTime().getTime() > item.getExpireStart().getTime()) {
                            usableList.add(item);
                        }
                    }
                });
        return usableList;
    }


    /**
     * 更新过期优惠券状态
     *
     * @param mycouponList
     */
    private void updateExpiredCoupon(List<CouponDetail> mycouponList) {
        //已过了有效期到list
        List<Long> expiredIds = mycouponList.stream().filter(item -> {
            if (item.getExpirePeriod().equals(CouponExpireType.FIX_DATA.getCode())) {
                //如果是固定间隔日期，则判断当前天数是否过期
                if ((item.getAssignTime().getTime() / 1000
                        + (item.getExpireGap() * 24 * 3600))
                        > new Date().getTime() / 1000) {
                    return false;
                } else {
                    return true;
                }
            } else {
                if (item.getAssignTime().getTime()
                        > item.getExpireEnd().getTime()) {
                    return true;
                } else {
                    return false;
                }
            }
        }).map(CouponDetail::getCouponId).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(expiredIds)) {
            couponMapper.expiredByIds(expiredIds);

        }
    }


    /**
     * 优惠券发放
     * 支付成功后分发下单成功优惠券
     * 1.分发购买指定商品优惠券
     * 2.分发满金额优惠券
     *
     * @param trade 交易信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void receiveCouponByPaySuccess(Trade trade) {

        List<CouponTemplate> templates = new ArrayList<>();
        List<CouponTemplate> goodsTemplates = templateMapper.selectGoodsTemplateByTradeId(trade.getId());
        templates.addAll(goodsTemplates);

        List<OrderStore> orderStores = orderStoreMapper.selectByTradeId(trade.getId());
        for (OrderStore orderStore : orderStores) {
            int amount = orderStore.getPrice().intValue() - orderStore.getSaveMoney() - orderStore.getFreight();
            List<CouponTemplate> list = templateMapper.selectTemplateByDiscountBase(amount, orderStore.getStoreId());
            templates.addAll(list);
        }

        //单个优惠券发放
        for (CouponTemplate template : templates) {
            try {
                receiveCoupon(template, trade.getBuyerId());
            } catch (LsException e) {
                log.error(e.getMessage() + "\n  template:{} ,buyerId:{}",
                        JSON.toJSONString(template), trade.getBuyerId());
            }

        }

    }


    /**
     * 获取新注册用户优惠券
     * @return
     */
    public void receiveRegisterCouponTemplate(Long buyerId){
        log.info("get new User couponTemplate with userId:"+buyerId);
        List<Long> templateIds = templateMapper.getTemplateIdsByType(CouponCategory.REGISTER_COUPON.getCode());
        for (Long templateId:templateIds) {
            log.info("couponTemlateId:"+templateId);
            receiveCoupon(templateId,buyerId);
        }
    }
    /**
     * 领取优惠券
     *
     * @param templateId
     * @param buyerId
     */
    public void receiveCoupon(Long templateId, Long buyerId) {
        CouponTemplate template = templateMapper.selectById(templateId);
        receiveCoupon(template, buyerId);
    }

    /**
     * 领取优惠券
     *
     * @param template
     * @param buyerId
     */
    private void receiveCoupon(CouponTemplate template, Long buyerId) {
        // 数据加乐观锁，防止优惠券多发
        template = templateMapper.selectByIdWithLock(template.getId());

        checkReceive(template, buyerId);
        Coupon coupon = new Coupon();
        coupon.setTemplateId(template.getId());
        coupon.setCode(template.getCode() + buyerId + MakeOrderNum.getOrderNum());
        coupon.setAssignTime(new Date());
        coupon.setStatus(1);
        coupon.setUserId(buyerId);
        coupon.setStoreId(template.getStoreId());
        coupon.setCreateTime(new Date());
        couponMapper.insert(coupon);
        template.setDistributeCount(template.getDistributeCount() + 1);
        templateMapper.updateById(template);
    }

    /**
     * 检查当前用户是否可以领取该优惠券
     * 1.检查发放时间
     * 2。检查每人限领
     * 3.检查每天限领
     *
     * @param template
     * @param buyerId
     */
    private void checkReceive(CouponTemplate template, Long buyerId) throws LsException {

        if (template.getCount() != 0 && template.getCount()<=template.getDistributeCount()) {
            throw new LsException(CodeEnum.ERROR, "优惠券已发完");
        }
        if (!template.getAvailable()) {
            throw new LsException(CodeEnum.ERROR, "优惠券未开始发放");
        }

        Date now = new Date();
        if (template.getDistributeStart().after(now)
                || template.getDistributeEnd().before(now)) {
            throw new LsException(CodeEnum.ERROR, "该优惠券当前时间无法发放");
        }

        List<Coupon> list = couponMapper.selectByTemplateIdAndUserId(template.getId(), buyerId);
        if (!CollectionUtils.isEmpty(list)) {
            // 检查最大领取次数
            if (template.getDistributeUserLimite() != null && !template.getDistributeUserLimite().equals(0)) {
                if (list.size() >= template.getDistributeUserLimite()) {
                    throw new LsException(CodeEnum.ERROR, "该优惠券已经领取了"
                            + template.getDistributeUserLimite() + "次，无法再次领取");
                }
            }

            //检查单天领取最大次数
            if (template.getDistributeDayLimite() != null && !template.getDistributeDayLimite().equals(0)) {
                List<Coupon> nowList = list.stream()
                        .filter(item -> item.getAssignTime().getYear() == now.getYear()
                                && item.getAssignTime().getMonth() == now.getMonth()
                                && item.getAssignTime().getDay() == now.getDay())
                        .collect(Collectors.toList());

                if (nowList.size() >= template.getDistributeDayLimite()) {
                    throw new LsException(CodeEnum.ERROR, "该优惠券今天已经领取了"
                            + nowList.size() + "次，今天无法再次领取。请明天再试");
                }
            }
        }
    }


    /**
     * 计算全场满减优惠券的具体优惠价格
     * 1. 计算总金额是否达到满减基准
     * 2. 银行家舍入法进行四舍五入
     * 银行家舍入法：
     * 舍去位的数值小于5时，直接舍去。
     * <p>
     * 舍去位的数值大于5时，进位后舍去。
     * <p>
     * 当舍去位的数值等于5时，若5后面还有其他非0数值，则进位后舍去，若5后面是0时，则根据5前一位数的奇偶性来判断，奇数进位，偶数舍去。
     *
     * @param goodsInfoDos
     * @param couponDetail
     * @return
     */
    private CouponDetail amountUseSaveMoney(List<GoodsInfoDo> goodsInfoDos, CouponDetail couponDetail) {
        int sum = goodsInfoDos.stream()
                .mapToInt(item -> item.getPrice() * item.getNum())
                .sum();

        if (sum >= couponDetail.getDiscountBase()) {
            // 计算每件商品的优惠价格
            int sumSaveMoney = 0;
            for (int i = 0; i < goodsInfoDos.size(); i++) {
                GoodsInfoDo item = goodsInfoDos.get(i);
                BigDecimal saveMoney = new BigDecimal(
                        1.0 * item.getPrice() * item.getNum()
                                * couponDetail.getDiscountQuota() / sum

                );
                // 使用银行家舍入发进行舍入
                saveMoney = saveMoney.setScale(0, RoundingMode.HALF_EVEN);
                item.setSaveMoney(saveMoney.intValue());
                sumSaveMoney += item.getSaveMoney();
            }

            //平差价
            goodsInfoDos.get(0).setSaveMoney(
                    couponDetail.getDiscountQuota() - sumSaveMoney
                            + goodsInfoDos.get(0).getSaveMoney());

            return couponDetail;
        }
        return null;
    }

    /**
     * 计算按照商品发放优惠券的优惠金额，
     * 并且设置单个商品的优惠金额
     *
     * @param goodsInfoDos
     * @param couponDetail
     * @return
     */
    private CouponDetail goodsUseSaveMoney(List<GoodsInfoDo> goodsInfoDos, CouponDetail couponDetail) {
        List<Long> couponGoodsCommonIds = couponDetail.getUseGoods().stream()
                .map(item -> item.getGoodsCommonId()).collect(Collectors.toList());

        int sum = goodsInfoDos.stream().mapToInt(item -> {
            if (couponGoodsCommonIds.contains(item.getGoodsCommonId())) {
                return item.getPrice() * item.getNum();
            }
            return 0;
        }).sum();

        // 总结额大于满减基准，则修改每个单品优惠金额，返回优惠金额
        if (sum >= couponDetail.getDiscountBase()) {
            goodsInfoDos.forEach(item -> {
                if (couponGoodsCommonIds.contains(item.getGoodsCommonId())) {
                    BigDecimal saveMoney = new BigDecimal(
                            1.0 * item.getPrice() * item.getNum() / sum
                                    * couponDetail.getDiscountQuota()
                    );
                    // 使用银行家舍入发进行舍入
                    saveMoney = saveMoney.setScale(0, RoundingMode.HALF_EVEN);
                    item.setSaveMoney(saveMoney.intValue());
                } else {
                    item.setSaveMoney(0);
                }
            });

            //平差价
            int sumSaveMoney = goodsInfoDos.stream().mapToInt(GoodsInfoDo::getSaveMoney).sum();
            if (sumSaveMoney != couponDetail.getDiscountQuota()) {
                int difference = couponDetail.getDiscountQuota() - sumSaveMoney;
                for (GoodsInfoDo goodsInfoDo : goodsInfoDos) {
                    if (couponGoodsCommonIds.contains(goodsInfoDo.getGoodsCommonId())) {
                        goodsInfoDo.setSaveMoney(goodsInfoDo.getSaveMoney() + difference);
                        break;
                    }
                }
            }

            return couponDetail;
        }
        return null;
    }

    /**
     * 计算按照商品类型发放优惠券的优惠金额
     *
     * @param goodsInfoDos
     * @param couponDetail
     * @return
     */
    private CouponDetail goodsTypeUseSaveMoney(List<GoodsInfoDo> goodsInfoDos, CouponDetail couponDetail) {
        List<Integer> couponGoodsTypes = couponDetail.getUseGoods().stream()
                .map(item -> item.getGoodsType()).collect(Collectors.toList());
        int sum = goodsInfoDos.stream().mapToInt(item -> {
            if (couponGoodsTypes.contains(item.getTypeId())) {
                return item.getPrice() * item.getNum();
            }
            return 0;
        }).sum();

        // 总结额大于满减基准，则修改每个单品优惠金额，返回优惠金额
        if (sum >= couponDetail.getDiscountBase()) {
            goodsInfoDos.forEach(item -> {
                if (couponGoodsTypes.contains(item.getTypeId())) {
                    BigDecimal saveMoney = new BigDecimal(
                            1.0 * item.getPrice() * item.getNum() / sum
                                    * couponDetail.getDiscountQuota()
                    );
                    // 使用银行家舍入发进行舍入
                    saveMoney = saveMoney.setScale(0, RoundingMode.HALF_EVEN);
                    item.setSaveMoney(saveMoney.intValue());
                } else {
                    item.setSaveMoney(0);
                }
            });

            // 计算saveMoney之后，和优惠金额是否相等，如果不相等则找一个商品平价
            int sumSaveMoney = goodsInfoDos.stream().mapToInt(GoodsInfoDo::getSaveMoney).sum();
            if (sumSaveMoney != couponDetail.getDiscountQuota()) {
                int difference = couponDetail.getDiscountQuota() - sumSaveMoney;
                for (GoodsInfoDo goodsInfoDo : goodsInfoDos) {
                    if (couponGoodsTypes.contains(goodsInfoDo.getTypeId())) {
                        goodsInfoDo.setSaveMoney(goodsInfoDo.getSaveMoney() + difference);
                        break;
                    }
                }
            }

            return couponDetail;
        }
        return null;
    }


}
